Почему изменения в новом Phoenix 1.3 так важны / Хабр

您所在的位置:网站首页 phoenix framework Почему изменения в новом Phoenix 1.3 так важны / Хабр

Почему изменения в новом Phoenix 1.3 так важны / Хабр

#Почему изменения в новом Phoenix 1.3 так важны / Хабр| 来源: 网络整理| 查看: 265

Phoenix Framework всегда был классным. Но он никогда не был таким классным, как с новым релизом 1.3 (который сейчас находится в стадии RC2).

Произошло много значительных изменений. Крис МакКорд написал полный путеводитель по изменениям. Так же доступна его речь с LonestarElixir, где он подробно рассказывает про ключевые моменты. Вдохновленный его трудами, в своей статье я постараюсь рассказать вам про самые важные изменения в проекте Phoenix.

Давайте начнем!

Перевод выполнен самим автором оригинальной статьи Никитой Соболевым.

Существующие проблемы

Phoenix – новый фреймворк. И, естественно, у него есть некоторые проблемы. Основная команда работала очень старательно, чтобы решить некоторые из самых важных. Итак, каковы эти проблемы?

Директория web — чистая магия

При работе над проектом с использованием Phoenix у вас есть два места для исходного кода: lib/ и web/. Концепция такова:

Поместите всю свою бизнес-логику и утилиты внутрь lib/. Поместите всё, что связано с вашим веб-интерфейсом (контроллеры, представления, шаблоны) внутрь веб-каталога web/.

Но понятно ли это разработчикам? Я так не думаю.

Откуда появился этот веб-каталог? Это особенность Phoenix? Или другие фреймворки тоже используют его? Должен ли я использовать lib/ с Phoenix-проектами или он зарезервирован для некоторой глубинной магии? Все эти вопросы появились у меня после моей первой встречи с Phoenix.

До версии 1.2 только директория web/ автоматически перезагружалась. Итак, зачем мне создавать какие-либо файлы внутри lib/ и перезапускать сервер, когда я могу поместить их где-то внутри web/ для быстрой перезагрузки?

Это приводит нас к еще более важным вопросам: относятся ли мои файлы-модели (назовем их моделями в этом конкретном контексте) к web-части приложения или к основной логике? Можно ли разделить логику на разные домены или приложения (например, как в Django)?

Эти вопросы остаются без ответа.

Бизнес-логика в контроллерах

Более того, код шаблона, который идет в Phoenix, предполагает другой способ. Можно получить следующий код в новом проекте:

defmodule Example.UserController do use Example.Web, :controller # ... def update(conn, %{"id" => id, "user" => user_params}) do user = Repo.get!(User, id) changeset = User.changeset(user, user_params) case Repo.update(changeset) do {:ok, user} -> render(conn, Example.UserView, "show.json", user: user) {:error, changeset} -> conn |> put_status(:unprocessable_entity) |> render(Example.ChangesetView, "error.json", changeset: changeset) end end end

Что должен делать разработчик, когда пользователю после успешного обновления должно быть отправлено электронное письмо? Контроллер так и просится, чтобы его расширили. Просто поставьте еще одну строку кода перед render/4, что может пойти не так? Но. Только что Phoenix сам подтолкнул нас к неправильному использованию своей кодовой базы: мы пишем бизнес логику в контроллере!

На самом деле, одна дополнительная строка в контроллере это нормально. Все проблемы возникают, когда приложение растет. Таких строк становится много, приложение становится неустойчивым, неподъемным и повторяет само себя.

Схемы не являются моделями

В какой-то момент без особых причин схемы Ecto стали называться «моделями». В чем разница между «моделью» и «схемой»? Схема — это всего лишь способ определить структуру — структуру базы данных в данном конкретном случае. Модели как концепция намного сложнее схем. Модели должны обеспечивать способ управления данными и выполнять различные действия, как модели в Django или Rails. Elixir как функциональный язык не подходит для концепции «модели», поэтому они были упразднены в проекте Ecto.

Файлы внутри models/ не были организованы. По мере своего роста ваше приложение становится хаотичным. Как эти файлы связаны между собой? В каком контексте мы используем их? Это было трудно понять.

Кроме того, директория models/ рассматривалась как еще одно место для размещения вашей бизнес-логики, что нормально для других языков и фреймворков. Существует уже знакомая концепция «fat models». Но такая концепция, опять же, не подходит для Phoenix по уже названным причинам.

Решения

С момента последнего крупного релиза многое изменилось. Самый простой способ показать все изменения — на примере.

Требования

В этом руководстве предполагается, что у вас есть elixir-1.4, и он работает. Нет? Значит, установите его!

Установка

Для начала вам нужно будет установить новую версию Phoenix:

mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez Создание нового проекта

По завершению установки надо проверить, всё ли на месте. mix help вернет вам что-то вроде этого:

mix phoenix.new # Creates a new Phoenix v1.1.4 application mix phx.new # Creates a new Phoenix v1.3.0-rc.1 application using the experimental generators

Вот тут и проявляется первое изменение: новые генераторы. Старые генераторы назывались phoenix, а новые — просто phx. Теперь нужно меньше печатать. И, что более важно, новое сообщение разработчикам: эти генераторы новые, они будут делать что-то новое для вашего проекта.

Затем нужно создать структуру нового проекта, запустив:

mix phx.new medium_phx_example --no-html --no-brunch

Прежде чем мы увидим какие-либо результаты этой команды, давайте обсудим параметры. --no-html удаляет некоторые компоненты для работы с html, поэтому phx.gen.html больше не будет работать. Но мы строим json API, и нам не нужен html. Аналогично --no-brunch означает: не создавайте brunch-файл для работы со статикой.

Изменения Веб-директория

Глядя на ваши новые файлы, вы можете задаться вопросом: где находится веб-директория? Ну, вот и второе изменение. И довольно большое. Теперь ваша веб-директория находится внутри lib/. Она была особенной, многие люди неправильно поняли его главную цель, которая состояла в содержании веб-интерфейса для вашего приложения. Это не место для вашей бизнес-логики. Теперь все ясно. Поместите всё внутрь lib/. И оставьте только свои контроллеры, шаблоны и представления внутри новой web-директории. Вот как это выглядит:

lib └── medium_phx_example ├── application.ex ├── repo.ex └── web ├── channels │ └── user_socket.ex ├── controllers ├── endpoint.ex ├── gettext.ex ├── router.ex ├── views │ ├── error_helpers.ex │ └── error_view.ex └── web.ex

Где medium_phx_example — имя текущего приложения. Приложений может быть много. Итак, теперь весь код живет в одной и той же директории.

Третье изменение откроется вскоре после просмотра файла web.ex:

defmodule MediumPhxExample.Web do def controller do quote do use Phoenix.Controller, namespace: MediumPhxExample.Web import Plug.Conn # Before 1.3 it was just: # import MediumPhxExample.Router.Helpers import MediumPhxExample.Web.Router.Helpers import MediumPhxExample.Web.Gettext end end # Some extra code: # ... end

Phoenix теперь создает пространство имен .Web, которое очень хорошо сочетается с новой файловой структурой.

Создание схемы

Это четвертое и моё любимое изменение. Раньше у нас была директория web/models/, которая использовалась для хранения схем. Теперь концепция моделей полностью мертва. Внедрена новая философия:

схема представляет структуру данных; контекст используется для хранения нескольких схем; контекст используется для предоставления публичного внешнего API. Другими словами, он определяет, что можно сделать с вашими данными.

Наше приложение будет содержать только один контекст: Audio. Начнем с создания Audio контекста с двумя схемами Album и Song:

mix phx.gen.json Audio Album albums name:string release:utc_datetime mix phx.gen.json Audio Song songs album_id:references:audio_albums name:string duration:integer

Синтаксис этого генератора также изменился. Теперь требуется, чтобы имя контекста было первым аргументом. Также обратите внимание на audio_albums, схемы теперь содержат префикс с именем контекста. И вот что происходит со структурой проекта после запуска двух генераторов:

lib └── medium_phx_example ├── application.ex ├── audio │ ├── album.ex │ ├── audio.ex │ └── song.ex ├── repo.ex └── web ├── channels │ └── user_socket.ex ├── controllers │ ├── album_controller.ex │ ├── fallback_controller.ex │ └── song_controller.ex ├── endpoint.ex ├── gettext.ex ├── router.ex ├── views │ ├── album_view.ex │ ├── changeset_view.ex │ ├── error_helpers.ex │ ├── error_view.ex │ └── song_view.ex └── web.ex

Каковы основные изменения в структурах по сравнению с предыдущей версией?

Теперь схемы не принадлежат web/, а директория models/ вообще исчезла. Схемы теперь разделены контекстом, который определяет, как они связаны друг с другом.

И схемы прямо сейчас являются не более чем описанием таблицы. Чем и должна быть схема в первую очередь. Вот как выглядят наши схемы:

defmodule MediumPhxExample.Audio.Album do use Ecto.Schema schema "audio_albums" do field :name, :string field :release, :utc_datetime timestamps() end end defmodule MediumPhxExample.Audio.Song do use Ecto.Schema schema "audio_songs" do field :duration, :integer field :name, :string field :album_id, :id timestamps() end end

Всё за исключением самой схемы исчезло. Нет обязательных полей, никаких функций changeset/2 или каких-либо других. Генератор теперь даже не создает belongs_to для вас. Вы сами управляете связями ваших схем.

Итак, теперь это довольно ясно: схема — не место для вашей бизнес-логики. Всё это обрабатывается контекстом, который выглядит следующим образом:

defmodule MediumPhxExample.Audio do @moduledoc """ The boundary for the Audio system. """ import Ecto.{Query, Changeset}, warn: false alias MediumPhxExample.Repo alias MediumPhxExample.Audio.Album def list_albums do Repo.all(Album) end def get_album!(id), do: Repo.get!(Album, id) def create_album(attrs \\ %{}) do %Album{} |> album_changeset(attrs) |> Repo.insert() end # ... defp album_changeset(%Album{} = album, attrs) do album |> cast(attrs, [:name, :release]) |> validate_required([:name, :release]) end alias MediumPhxExample.Audio.Song def list_songs do Repo.all(Song) end def get_song!(id), do: Repo.get!(Song, id) def create_song(attrs \\ %{}) do %Song{} |> song_changeset(attrs) |> Repo.insert() end # ... defp song_changeset(%Song{} = song, attrs) do song |> cast(attrs, [:name, :duration]) |> validate_required([:name, :duration]) end end

Сам вид контекста отправляет ясный посыл: вот место, где нужно поместить свой код! Но будьте осторожны, файлы контекста могут разрастись. Разделите их на несколько модулей в таком случае.

Использование контроллера

Раньше у нас было много кода в контроллере по-умолчанию и разработчику было легко расширить шаблонный код. Здесь появляется пятое изменение. Начиная с нового выпуска, шаблонный код в контроллере был уменьшен и реорганизован:

defmodule MediumPhxExample.Web.AlbumController do use MediumPhxExample.Web, :controller alias MediumPhxExample.Audio alias MediumPhxExample.Audio.Album action_fallback MediumPhxExample.Web.FallbackController # ... def update(conn, %{"id" => id, "album" => album_params}) do album = Audio.get_album!(id) with {:ok, %Album{} = album} put_status(:unprocessable_entity) |> render(MediumPhxExample.Web.ChangesetView, "error.json", changeset: changeset) end def call(conn, {:error, :not_found}) do conn |> put_status(:not_found) |> render(MediumPhxExample.Web.ErrorView, :"404") end end

Что происходит, когда результат из Audio.update_album(album, album_params) не соответствует {:ok, %Album{} = album}? В этой ситуации вызывается контроллер, определенный в action_fallback. И будет выбран правильный call/2, что в свою очередь возвращает правильный ответ. Легко и приятно. Никаких обработок исключений в контроллере.

Заключение

Внесенные изменения весьма интересны. Их много, они все сфокусированы на том, чтобы загубить старые привычки программистов, которые пришли из других языков программирования. И новые изменения стараются пополнить философию Phoenix-Way новыми практиками. Надеюсь, эта статья была полезна и побудила вас использовать Phoenix Framework по максимуму. Заходите ко мне на GitHub.

Благодарим Никиту за подготовку перевода своей собственной оригинальной статьи и с радостью публикуем материал на Хабре. Никита представляет сообщество ElixirLangMoscow, которое организует митапы по Эликсиру в Москве, а также является активным контрибьютером в опенсорс и вносит значительный вклад в наше сообщество Вунш. На сайте вас ждут 3 десятка тематических статей, еженедельная рассылка и новости из мира Эликсира. А для вопросов у нас есть чат в Телеграме с отличными участниками.



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3